package com.linkhub.oms.admin.modules.stock.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.linkhub.oms.admin.common.exception.ExceptionFactory;
import com.linkhub.oms.admin.modules.order.service.CustomerOrderBizService;
import com.linkhub.oms.admin.modules.stock.domain.param.OutStockQueryParam;
import com.linkhub.oms.admin.modules.stock.domain.vo.OutStockVO;
import com.linkhub.oms.admin.modules.stock.service.OutStockBizService;
import com.linkhub.oms.common.response.PageDTO;
import com.linkhub.oms.common.util.CacheKeyAssist;
import com.linkhub.oms.component.mp.util.QtMybatisPlusUtil;
import com.linkhub.oms.component.redis.locker.DistributedLocker;
import com.linkhub.oms.dao.mapper.OutStockEntityMapper;
import com.linkhub.oms.entity.*;
import com.linkhub.oms.entity.resulttype.stock.OutStockResultEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * @ClassName OutStockBizServiceImpl
 * @Description TODO
 * @Author XieYu
 * @Date 2024/7/1 23:34
 * @Version 1.0
 */
@Slf4j
@Service
public class OutStockBizServiceImpl implements OutStockBizService {
    @Resource
    private OutStockEntityMapper outStockMapper;
    @Resource
    private DistributedLocker distributedLocker;

    @Override
    public PageDTO<OutStockVO> list(OutStockQueryParam queryParam) {
        MPJLambdaWrapper<OutStockEntity> outStockWrapper = JoinWrappers.lambda(OutStockEntity.class);
        outStockWrapper.selectAs(OutStockEntity::getStashName, OutStockResultEntity::getStashName)
                .selectAs(OutStockEntity::getSkuId, OutStockResultEntity::getSkuId)
                .selectAs(LocalProductEntity::getSku, OutStockResultEntity::getSku)
                .selectAs(LocalProductEntity::getImgUrl, OutStockResultEntity::getImgUrl)
                .selectAs(LocalProductEntity::getProductEnTitle, OutStockResultEntity::getProductEnTitle)
                .selectAs(LocalProductEntity::getSpu, OutStockResultEntity::getSpu)
                .selectAs(UserEntity::getUserName, OutStockResultEntity::getMerchantUserName)
                .selectAs(OutStockEntity::getAmount, OutStockResultEntity::getOutAmount)
                .innerJoin(LocalProductEntity.class, on -> on.eq(BatchEntity::getSkuId, LocalProductEntity::getSkuId))
                .innerJoin(UserEntity.class, on -> on.eq(OutStockEntity::getUserId, UserEntity::getUserId))
                .like(StringUtils.isNotBlank(queryParam.getSku()), LocalProductEntity::getSku, queryParam.getSku())
                .like(StringUtils.isNotBlank(queryParam.getSpu()), LocalProductEntity::getSpu, queryParam.getSpu())
                .like(StringUtils.isNotBlank(queryParam.getProductEnTitle()), LocalProductEntity::getProductEnTitle, queryParam.getProductEnTitle())
                .in(CollectionUtils.isNotEmpty(queryParam.getMerchantUserIds()), UserEntity::getUserId, queryParam.getMerchantUserIds())
                .gt(OutStockEntity::getAmount, 0);

        Page<OutStockResultEntity> objectPage = new Page<>(queryParam.getPage(), queryParam.getSize());
        objectPage.setOptimizeJoinOfCountSql(false);

        Page<OutStockResultEntity> page = outStockMapper.selectJoinPage(objectPage, OutStockResultEntity.class, outStockWrapper);

        return QtMybatisPlusUtil.convertPage(page, OutStockVO::convert);
    }

    private void executeByLock(String userId, String skuId, String stashName, Runnable runnable) {
        String lockKey = CacheKeyAssist.getOutStockUpdatedLockKey(userId, skuId, stashName);
        if (!distributedLocker.tryLock(lockKey)) {
            log.error("更新缺货时获取锁失败");
            throw ExceptionFactory.getI18nBizException("stock.out.updated.concurrence.error");
        }
        try {
            runnable.run();
        } finally {
            distributedLocker.unlock(lockKey);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void addStock(String userId, String skuId, String stashName, Integer outAmount) {
        log.info("增加缺货数量，userId: {}, skuId: {}, 仓库：{}, 数量：{}", userId, skuId, stashName, outAmount);
        executeByLock(userId, skuId, stashName, () -> {
            LambdaQueryWrapper<OutStockEntity> outStockWrapper = new LambdaQueryWrapper<>();
            outStockWrapper.eq(OutStockEntity::getUserId, userId);;
            outStockWrapper.eq(OutStockEntity::getSkuId, skuId);
            outStockWrapper.eq(OutStockEntity::getStashName, stashName);
            OutStockEntity existOutStock = outStockMapper.selectOne(outStockWrapper);
            if (Objects.nonNull(existOutStock)) {
                existOutStock.setAmount(existOutStock.getAmount() + outAmount);
                outStockMapper.updateById(existOutStock);
            } else {
                OutStockEntity outStockEntity = new OutStockEntity();
                outStockEntity.setUserId(userId);
                outStockEntity.setStashName(stashName);
                outStockEntity.setSkuId(skuId);
                outStockEntity.setAmount(outAmount);
                outStockMapper.insert(outStockEntity);
            }
        });
    }

    @Resource
    private CustomerOrderBizService customerOrderBizService;

    @Transactional(rollbackFor = Exception.class)
    public void minusStock(String userId, String skuId, String stashName, Integer addAmount) {
        executeByLock(userId, skuId, stashName, () -> {
            LambdaQueryWrapper<OutStockEntity> outStockWrapper = new LambdaQueryWrapper<>();
            outStockWrapper.eq(OutStockEntity::getUserId, userId);
            outStockWrapper.eq(OutStockEntity::getSkuId, skuId);
            outStockWrapper.eq(OutStockEntity::getStashName, stashName);

            OutStockEntity existOutStock = outStockMapper.selectOne(outStockWrapper);
            if (Objects.nonNull(existOutStock)) {
                int updateAmount = existOutStock.getAmount() - addAmount;
                if (updateAmount < 0) {
                    updateAmount = 0;
                }
                existOutStock.setAmount(updateAmount);
                outStockMapper.updateById(existOutStock);
            }

            try {
                customerOrderBizService.reSubmit(userId, skuId, stashName);
            } catch (Exception e) {
                log.error("订单重新预占失败了", e);
            }
        });
    }
}
